local Family = require"ecs.family"

local mt = {}
mt.__index = mt

function mt.New(cls)
    local self = setmetatable({},cls)
    self._families = {} --{familyId=family}
    self._familesIndex = {} -- {componentName=[family]}
    self._systems = {} --[]
    self._entities = {}

    self.singletonCom = {} --{name=component}
    return self
end

function mt:update(dt)
    for idx,system in ipairs(self._systems) do
        system:update(dt)
    end
end

function mt:addSystem(system)
    table.insert(self._systems, system)
    system:addToWorld(self)
end

function mt:removeFromWorld(system)
    for idx,sys in ipairs(self._systems) do
        if sys == system then
            table.remove(self._systems, idx)
            system:removeFromWorld(self)
            break
        end
    end
end

function mt:addEntity(entity)
    for name,component in pairs(entity._components) do
        if not self._familesIndex[name] then
            self._familesIndex[name] = {}
        end
        local families = self._familesIndex[name]
        for _,family in ipairs(families) do
            family:addEntityIfMatch(entity)
        end
    end

    entity.onComponentAdded:add(function (entity, componentName)
        self:_onComponentAdded(entity, componentName)
    end)
    entity.onComponentRemoved:add(function (entity, componentName)
        self:_onComponentRemoved(entity, componentName)
    end)

    self._entities[entity.id] = entity
end

function mt:removeEntity(entity)
    for name,component in pairs(entity._components) do
        if not self._familesIndex[name] then
            self._familesIndex[name] = {}
        end
        local families = self._familesIndex[name]
        for _,family in ipairs(families) do
            family:removeEntity(entity)
        end
    end
    self._entities[entity.id] = nil
    entity.removed = true
end

function mt:getEntity(id)
    return self._entities[id]
end

function mt:getEntities(componentNames)
    local familyId = self:_getFamilyId(componentNames)
    self:_ensureFamilyExists(componentNames, familyId)
    return self._families[familyId]:getEntities()
end

function mt:_onComponentAdded(entity,componentName)
    if not self._familesIndex[componentName] then
        self._familesIndex[componentName] = {}
    end
    local families = self._familesIndex[componentName]
    for _,family in ipairs(families) do
        family:onComponentAdded(entity, componentName)
    end
end

function mt:_onComponentRemoved(entity,componentName)
    if not self._familesIndex[componentName] then
        self._familesIndex[componentName] = {}
    end
    local families = self._familesIndex[componentName]
    for _,family in ipairs(families) do
        family:onComponentRemoved(entity, componentName)
    end
end

-- 返回信号器实例
function mt:entityAdded(componentNames)
    local components = {}
    if type(componentNames) == "string" then
        table.insert(components,componentNames)
    else
        components = componentNames
    end
    local familyId = self:_getFamilyId(components)
    self:_ensureFamilyExists(components, familyId)
    return self._families[familyId].entityAdded
end

-- 返回信号器实例
function mt:entityRemoved(componentNames)
    local components = {}
    if type(componentNames) == "string" then
        table.insert(components,componentNames)
    else
        components = componentNames
    end
    local familyId = self:_getFamilyId(components)
    self:_ensureFamilyExists(components, familyId)
    return self._families[familyId].entityRemoved
end

function mt:_ensureFamilyExists(componentNames, familyId)
    local familyId = familyId or self:_getFamilyId(componentNames)
    if not self._families[familyId] then
        self._families[familyId] = Family:New(componentNames)

        -- 缓存 组件映射到家庭集合
        for idx, name in ipairs(componentNames) do
            if not self._familesIndex[name] then
                self._familesIndex[name] = {} --[]
            end
            table.insert(self._familesIndex[name], self._families[familyId])
        end

        -- 检查世界中的entity,如果有匹配家庭集合的都增加进去
        for id,entity in pairs(self._entities) do
            self._families[FamilyId]:addEntityIfMatch(entity)
        end
    end
end

function mt:_getFamilyId(componentNames)
    return "$"..table.concat(componentNames,",")
end

return mt
